/*
 * Copyright (c) 2016 Ha Duy Trung
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.github.hidroh.materialistic.widget;

import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.os.Build;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v4.util.ArrayMap;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import io.github.hidroh.materialistic.AppUtils;
import io.github.hidroh.materialistic.ComposeActivity;
import io.github.hidroh.materialistic.MenuTintDelegate;
import io.github.hidroh.materialistic.R;
import io.github.hidroh.materialistic.accounts.UserServices;
import io.github.hidroh.materialistic.annotation.Synthetic;
import io.github.hidroh.materialistic.data.Favorite;
import io.github.hidroh.materialistic.data.ItemManager;

public class FavoriteRecyclerViewAdapter extends ListRecyclerViewAdapter
        <ListRecyclerViewAdapter.ItemViewHolder, Favorite> {

    public interface ActionModeDelegate {

        boolean startActionMode(ActionMode.Callback callback);
        boolean isInActionMode();
        void stopActionMode();
    }

    private ItemTouchHelper mItemTouchHelper;
    private final ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
        private boolean mPendingClear;

        @Override
        public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
            actionMode.getMenuInflater().inflate(R.menu.menu_favorite_action, menu);
            mMenuTintDelegate.onOptionsMenuCreated(menu);
            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
            return false;
        }

        @Override
        public boolean onActionItemClicked(final ActionMode actionMode, MenuItem menuItem) {
            if (menuItem.getItemId() == R.id.menu_clear) {
                mAlertDialogBuilder
                        .init(mContext)
                        .setMessage(R.string.confirm_clear_selected)
                        .setPositiveButton(android.R.string.ok,
                                (dialog, which) -> {
                                    mPendingClear = true;
                                    removeSelection();
                                    actionMode.finish();
                                })
                        .setNegativeButton(android.R.string.cancel, null)
                        .create()
                        .show();
                return true;
            }

            return false;
        }

        @Override
        public void onDestroyActionMode(ActionMode actionMode) {
            if (!isAttached()) {
                return;
            }
            mActionModeDelegate.stopActionMode();
            if (mPendingClear) {
                mPendingClear = false;
            } else {
                mSelected.clear();
            }
            notifyDataSetChanged();
        }
    };
    @Synthetic ActionModeDelegate mActionModeDelegate;
    @Synthetic MenuTintDelegate mMenuTintDelegate;
    @Synthetic ArrayMap<Integer, String> mSelected = new ArrayMap<>();
    private int mPendingAdd = -1;

    public FavoriteRecyclerViewAdapter(ActionModeDelegate actionModeDelegate) {
        super();
        mActionModeDelegate = actionModeDelegate;
    }

    @Override
    public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        mMenuTintDelegate = new MenuTintDelegate();
        mMenuTintDelegate.onActivityCreated(mContext);
        mItemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback(mContext) {
            @Override
            public int getSwipeDirs(RecyclerView recyclerView,
                                    RecyclerView.ViewHolder viewHolder) {
                if (!mSwipeEnabled) {
                    return 0;
                }
                if (mActionModeDelegate != null && mActionModeDelegate.isInActionMode()) {
                    return 0;
                }
                return super.getSwipeDirs(recyclerView, viewHolder);
            }

            @Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
                dismiss(viewHolder.getAdapterPosition());
            }
        });
        mItemTouchHelper.attachToRecyclerView(recyclerView);
    }

    @Override
    public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
        super.onDetachedFromRecyclerView(recyclerView);
        mItemTouchHelper.attachToRecyclerView(null);
        mActionModeDelegate = null;
    }

    @Override
    protected ItemViewHolder create(ViewGroup parent, int viewType) {
        return new ItemViewHolder(mInflater.inflate(R.layout.item_favorite, parent, false));
    }

    @Override
    public int getItemCount() {
        return mFavoriteManager.getSize();
    }

    @Override
    protected void bindItem(final ItemViewHolder holder) {
        final Favorite favorite = getItem(holder.getAdapterPosition());
        holder.itemView.setOnLongClickListener(v -> {
            if (mActionModeDelegate.startActionMode(mActionModeCallback)) {
                toggle(favorite.getId(), holder.getAdapterPosition());
                return true;
            }

            return false;
        });
        holder.mStoryView.getMoreOptions().setOnClickListener(v -> showMoreOptions(v, favorite));
    }

    @Override
    protected boolean isItemAvailable(Favorite item) {
        return item != null;
    }

    @Override
    protected void handleItemClick(Favorite item, ItemViewHolder holder) {
        if (!mActionModeDelegate.isInActionMode()) {
            super.handleItemClick(item, holder);
        } else {
            toggle(item.getId(), holder.getAdapterPosition());
        }
    }

    @Override
    protected Favorite getItem(int position) {
        return mFavoriteManager.getItem(position);
    }

    @Override
    protected boolean isSelected(String itemId) {
        return super.isSelected(itemId) || mSelected.containsValue(itemId);
    }

    @Override
    protected int getItemCacheMode() {
        return ItemManager.MODE_CACHE;
    }

    public void notifyChanged() {
        if (getItemCount() == 0) {
            notifyDataSetChanged();
            return;
        }
        if (!mSelected.isEmpty()) { // has pending removals, notify removed
            List<Integer> positions = new ArrayList<>(mSelected.keySet());
            Collections.sort(positions);
            mSelected.clear();
            for (int i = positions.size() - 1; i >= 0; i--) {
                notifyItemRemoved(positions.get(i));
            }
        } else if (mPendingAdd >= 0) { // has pending insertion, notify inserted
            notifyItemInserted(mPendingAdd);
            mPendingAdd = -1;
        } else { // no pending changes, simply refresh list
            notifyDataSetChanged();
        }
    }

    @Synthetic
    void removeSelection() {
        mFavoriteManager.remove(mContext, mSelected.values());
    }

    @Synthetic
    void dismiss(final int position) {
        final Favorite item = getItem(position);
        mSelected.put(position, item.getId());
        mFavoriteManager.remove(mContext, mSelected.values());
        Snackbar.make(mRecyclerView, R.string.toast_removed, Snackbar.LENGTH_LONG)
                .setAction(R.string.undo, v -> {
                    mPendingAdd = position;
                    mFavoriteManager.add(mContext, item);
                })
                .show();
    }

    private void toggle(String itemId, int position) {
        if (mSelected.containsValue(itemId)) {
            mSelected.remove(position);
        } else {
            mSelected.put(position, itemId);
        }
        notifyItemChanged(position);
    }

    private void showMoreOptions(View v, final Favorite item) {
        mPopupMenu.create(mContext, v, Gravity.NO_GRAVITY)
                .inflate(R.menu.menu_contextual_favorite)
                .setOnMenuItemClickListener(menuItem -> {
                    if (menuItem.getItemId() == R.id.menu_contextual_vote) {
                        vote(item);
                        return true;
                    }
                    if (menuItem.getItemId() == R.id.menu_contextual_comment) {
                        mContext.startActivity(new Intent(mContext, ComposeActivity.class)
                                .putExtra(ComposeActivity.EXTRA_PARENT_ID, item.getId())
                                .putExtra(ComposeActivity.EXTRA_PARENT_TEXT, item.getDisplayedTitle()));
                        return true;
                    }
                    if (menuItem.getItemId() == R.id.menu_contextual_share) {
                        mContext.startActivity(AppUtils.makeChooserShareIntent(mContext,
                                item.getDisplayedTitle(),
                                item.getUrl()));
                        return true;
                    }
                    return false;
                })
                .show();
    }

    private void vote(final Favorite item) {
        mUserServices.voteUp(mContext, item.getId(), new VoteCallback(this));
    }

    @Synthetic
    void onVoted(Boolean successful) {
        if (successful == null) {
            Toast.makeText(mContext, R.string.vote_failed, Toast.LENGTH_SHORT).show();
        } else if (successful) {
            Toast.makeText(mContext, R.string.voted, Toast.LENGTH_SHORT).show();
        } else {
            AppUtils.showLogin(mContext, mAlertDialogBuilder);
        }
    }

    static class VoteCallback extends UserServices.Callback {
        private final WeakReference<FavoriteRecyclerViewAdapter> mAdapter;

        @Synthetic
        VoteCallback(FavoriteRecyclerViewAdapter adapter) {
            mAdapter = new WeakReference<>(adapter);
        }

        @Override
        public void onDone(boolean successful) {
            if (mAdapter.get() != null && mAdapter.get().isAttached()) {
                mAdapter.get().onVoted(successful);
            }
        }

        @Override
        public void onError(Throwable throwable) {
            if (mAdapter.get() != null && mAdapter.get().isAttached()) {
                mAdapter.get().onVoted(null);
            }
        }
    }

    static abstract class ItemTouchHelperCallback extends PeekabooTouchHelperCallback {

        private final String mText;
        private final int mColor;

        ItemTouchHelperCallback(Context context) {
            super(context);
            mText = context.getString(R.string.delete);
            mColor = ContextCompat.getColor(context, R.color.red500);
        }

        @Override
        public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                float alpha = 1 - Math.abs(dX) / viewHolder.itemView.getWidth();
                viewHolder.itemView.setAlpha(alpha);
            }
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        }

        @Override
        protected String getLeftText() {
            return mText;
        }

        @Override
        protected String getRightText() {
            return mText;
        }

        @Override
        protected int getLeftTextColor() {
            return mColor;
        }

        @Override
        protected int getRightTextColor() {
            return mColor;
        }
    }
}
